home *** CD-ROM | disk | FTP | other *** search
/ Gold Medal Software 2 / Gold Medal Software Volume 2 (Gold Medal) (1994).iso / archive / ntzip.arj / ZIPFILE.C < prev    next >
C/C++ Source or Header  |  1992-03-26  |  21KB  |  663 lines

  1. /*
  2.  
  3.  Copyright (C) 1990,1991 Mark Adler, Richard B. Wales, and Jean-loup Gailly.
  4.  Permission is granted to any individual or institution to use, copy, or
  5.  redistribute this software so long as all of the original files are included
  6.  unmodified, that it is not sold for profit, and that this copyright notice
  7.  is retained.
  8.  
  9. */
  10.  
  11. /*
  12.  *  zipfile.c by Mark Adler.
  13.  */
  14.  
  15. #include "zip.h"
  16.  
  17.  
  18. /* Macros for converting integers in little-endian to machine format */
  19. #define SH(a) (((ush)(uch)(a)[0]) | (((ush)(uch)(a)[1]) << 8))
  20. #define LG(a) ((ulg)SH(a) | ((ulg)SH((a)+2) << 16))
  21.  
  22. /* Macros for writing machine integers to little-endian format */
  23. #define PUTSH(a,f) {putc((char)(a),(f)); putc((char)((a) >> 8),(f));}
  24. #define PUTLG(a,f) {PUTSH(a,f) PUTSH((a) >> 16,f)}
  25.  
  26.  
  27. /* -- Structure of a ZIP file -- */
  28.  
  29. /* Signatures for zip file information headers */
  30. #define LOCSIG  0x04034b50L
  31. #define CENSIG  0x02014b50L
  32. #define ENDSIG  0x06054b50L
  33.  
  34. /* Offsets of values in headers */
  35. #define LOCVER  0               /* version needed to extract */
  36. #define LOCFLG  2               /* encrypt, implosion flags */
  37. #define LOCHOW  4               /* compression method */
  38. #define LOCTIM  6               /* last modified file time, DOS format */
  39. #define LOCDAT  8               /* last modified file date, DOS format */
  40. #define LOCCRC  10              /* uncompressed crc-32 for file */
  41. #define LOCSIZ  14              /* compressed size in zip file */
  42. #define LOCLEN  18              /* uncompressed size */
  43. #define LOCNAM  22              /* length of filename */
  44. #define LOCEXT  24              /* length of extra field */
  45.  
  46. #define CENVEM  0               /* version made by */
  47. #define CENVER  2               /* version needed to extract */
  48. #define CENFLG  4               /* encrypt, implosion flags */
  49. #define CENHOW  6               /* compression method */
  50. #define CENTIM  8               /* last modified file time, DOS format */
  51. #define CENDAT  10              /* last modified file date, DOS format */
  52. #define CENCRC  12              /* uncompressed crc-32 for file */
  53. #define CENSIZ  16              /* compressed size in zip file */
  54. #define CENLEN  20              /* uncompressed size */
  55. #define CENNAM  24              /* length of filename */
  56. #define CENEXT  26              /* length of extra field */
  57. #define CENCOM  28              /* file comment length */
  58. #define CENDSK  30              /* disk number start */
  59. #define CENATT  32              /* internal file attributes */
  60. #define CENATX  34              /* external file attributes */
  61. #define CENOFF  38              /* relative offset of local header */
  62.  
  63. #define ENDDSK  0               /* number of this disk */
  64. #define ENDBEG  2               /* number of the starting disk */
  65. #define ENDSUB  4               /* entries on this disk */
  66. #define ENDTOT  6               /* total number of entries */
  67. #define ENDSIZ  8               /* size of entire central directory */
  68. #define ENDOFF  12              /* offset of central on starting disk */
  69. #define ENDCOM  16              /* length of zip file comment */
  70.  
  71.  
  72. /* Local functions */
  73. #ifdef PROTO
  74.    local int zqcmp(voidp *, voidp *);
  75. #  ifndef UTIL
  76.      local int zbcmp(voidp *, voidp far *);
  77.      local char *cutpath(char *);
  78. #  endif /* !UTIL */
  79. #endif /* PROTO */
  80.  
  81.  
  82. local int zqcmp(a, b)
  83. voidp *a, *b;           /* pointers to pointers to zip entries */
  84. /* Used by qsort() to compare entries in the zfile list.  */
  85. {
  86.   return strcmp((*(struct zlist far **)a)->zname,
  87.                 (*(struct zlist far **)b)->zname);
  88. }
  89.  
  90.  
  91. #ifndef UTIL
  92.  
  93. local int zbcmp(n, z)
  94. voidp *n;               /* string to search for */
  95. voidp far *z;           /* pointer to a pointer to a zip entry */
  96. /* Used by search() to compare a target to an entry in the zfile list. */
  97. {
  98. #ifdef OS2
  99.   return stricmp((char *)n, ((struct zlist far *)z)->zname);
  100. #else /* !OS2 */
  101.   return strcmp((char *)n, ((struct zlist far *)z)->zname);
  102. #endif /* ?OS2 */
  103. }
  104.  
  105.  
  106. struct zlist far *zsearch(n)
  107. char *n;                /* name to find */
  108. /* Return a pointer to the entry in zfile with the name n, or NULL if
  109.    not found. */
  110. {
  111.   voidp far **p;        /* result of search() */
  112.  
  113.   if (zcount && (p = search(n, (voidp far **)zsort, zcount, zbcmp)) != NULL)
  114.     return *(struct zlist far **)p;
  115.   else
  116.     return NULL;
  117. }
  118.  
  119. #endif /* !UTIL */
  120.  
  121.  
  122. #ifdef VMS
  123. #  define PATHCUT ']'
  124. #else /* !VMS */
  125. #  define PATHCUT '/'
  126. #endif /* VMS */
  127.  
  128. char *ziptyp(s)
  129. char *s;                /* file name to force to zip */
  130. /* If the file name *s has a dot (other than the first char), then return
  131.    the name, otherwise append .zip to the name.  Allocate the space for
  132.    the name in either case.  Return a pointer to the new name, or NULL
  133.    if malloc() fails. */
  134. {
  135.   char *q;              /* temporary pointer */
  136.   char *t;              /* pointer to malloc'ed string */
  137.  
  138.   if ((t = malloc(strlen(s) + 5)) == NULL)
  139.     return NULL;
  140.   strcpy(t, s);
  141. #ifdef MSDOS
  142.   for (q = t; *q; q++)
  143.     if (*q == '\\')
  144.       *q = '/';
  145. #endif /* MSDOS */
  146.   if (strrchr((q = strrchr(t, PATHCUT)) == NULL ? t : q + 1, '.') == NULL)
  147.     strcat(t, ".zip");
  148. #ifdef MSDOS
  149. #if !defined (OS2) && !defined (WIN32)
  150.   strupr(t);
  151. #endif /* !OS2 */
  152. #endif /* MSDOS */
  153.   return t;
  154. }
  155.  
  156.  
  157. int readzipfile()
  158. /*
  159.    Make first pass through zip file, reading information from local file
  160.    headers and then verifying that information with the central file
  161.    headers.  Any deviation from the expected zip file format returns an
  162.    error.  At the end, a sorted list of file names in the zip file is made
  163.    to facilitate searching by name.
  164.  
  165.    The name of the zip file is pointed to by the global "zipfile".  The
  166.    globals zfiles, zcount, zcomlen, zcomment, and zsort are filled in.
  167.    Return an error code in the ZE_ class.
  168. */
  169. {
  170.   char b[CENHEAD];      /* buffer for central headers */
  171.   FILE *f;              /* zip file */
  172.   int m;                /* mismatch flag */
  173.   extent n;             /* length of name */
  174.   ulg p;                /* current file offset */
  175.   char r;               /* holds reserved bits during memcmp() */
  176.   ulg s;                /* size of data, start of central */
  177.   char *t;              /* temporary variable */
  178.   char far *u;          /* temporary variable */
  179.   struct zlist far * far *x;    /* pointer last entry's link */
  180.   struct zlist far *z;  /* current zip entry structure */
  181.  
  182.   /* Initialize zip file info */
  183.   zipbeg = 0;
  184.   zfiles = NULL;                        /* Points to first header */
  185.   zcomlen = 0;                          /* zip file comment length */
  186.  
  187.   /* If zip file exists, read headers and check structure */
  188.   if ((f = fopen(zipfile, FOPR)) != NULL)
  189.   {
  190.     x = &zfiles;                        /* first link */
  191.     p = 0;                              /* starting file offset */
  192.     zcount = 0;                         /* number of files */
  193.  
  194.     /* Find start of zip structures */
  195.     while (fread(b, 4, 1, f) == 1 && (s = LG(b)) != LOCSIG && s != ENDSIG)
  196.       if (fseek(f, -3L, SEEK_CUR))
  197.         return ferror(f) ? ZE_READ : ZE_EOF;
  198.       else
  199.         p++;
  200.     zipbeg = p;
  201.  
  202.     /* Read local headers */
  203.     while (LG(b) == LOCSIG)
  204.     {
  205.       /* Read local header raw to compare later with central header 
  206.          (this requires that the offest of ext in the zlist structure
  207.          be greater than or equal to LOCHEAD) */
  208.       if ((z = (struct zlist far *)farmalloc(sizeof(struct zlist))) == NULL)
  209.         return ZE_MEM;
  210.       if (fread(b, LOCHEAD, 1, f) != 1)
  211.         return ferror(f) ? ZE_READ : ZE_EOF;
  212.       t = b;  u = (char far *)z;  n = LOCHEAD;
  213.       do {
  214.         *u++ = *t++;
  215.       } while (--n);
  216.  
  217.       /* Link into list */
  218.       *x = z;
  219.       z->nxt = NULL;
  220.       x = &z->nxt;
  221.  
  222.       /* Read file name and extra field and skip data */
  223.       n = SH(LOCNAM + (uch far *)z);
  224.       z->ext = SH(LOCEXT + (uch far *)z);
  225.       s = LG(LOCSIZ + (uch far *)z);
  226.       if (n == 0)
  227.       {
  228.         sprintf(errbuf, "%d", zcount + 1);
  229.         warn("zero-length name for entry #", errbuf);
  230.         return ZE_FORM;
  231.       }
  232.       if ((z->zname = malloc(n+1)) ==  NULL ||
  233.           (z->ext && (z->extra = malloc(z->ext)) == NULL))
  234.         return ZE_MEM;
  235.       if (fread(z->zname, n, 1, f) != 1 ||
  236.           (z->ext && fread(z->extra, z->ext, 1, f) != 1) ||
  237.           fseek(f, (long)s, SEEK_CUR))
  238.         return ferror(f) ? ZE_READ : ZE_EOF;
  239.       z->zname[n] = 0;                  /* terminate name */
  240. #ifdef UTIL
  241.       z->name = z->zname;
  242. #else /* !UTIL */
  243.       z->name = in2ex(z->zname);        /* convert to external name */
  244.       if (z->name == NULL)
  245.         return ZE_MEM;
  246. #endif /* ?UTIL */
  247.  
  248.       /* Save offset, update for next header */
  249.       z->off = p;
  250.       p += 4 + LOCHEAD + n + z->ext + s;
  251.       zcount++;
  252.  
  253.       /* Read next signature */
  254.       if (fread(b, 4, 1, f) != 1)
  255.         return ferror(f) ? ZE_READ : ZE_EOF;
  256.     }
  257.  
  258.     /* Point to start of header list and read central headers */
  259.     z = zfiles;
  260.     s = p;                              /* save start of central */
  261.     while (LG(b) == CENSIG)
  262.     {
  263.       if (z == NULL)
  264.       {
  265.         warn("extraneous central header signature", "");
  266.         return ZE_FORM;
  267.       }
  268.  
  269.       /* Read central header */
  270.       if (fread(b, CENHEAD, 1, f) != 1)
  271.         return ferror(f) ? ZE_READ : ZE_EOF;
  272.  
  273.       /* Compare local header with that part of central header (except
  274.          for the reserved bits in the general purpose flags and except
  275.          for length of extra fields--authentication can make these
  276.          different in central and local headers) */
  277.       z->lflg = SH(LOCFLG + (uch far *)z);      /* Save reserved bits */
  278.       r = b[CENFLG+1];
  279.       ((uch far *)z)[LOCFLG+1] &= 0x1f; /* Zero out reserved bits */
  280.       b[CENFLG+1] &= 0x1f;
  281.       for (m = 0, u = (char far *)z, n = 0; n < LOCHEAD - 2; n++)
  282.         if (u[n] != b[n+2])
  283.         {
  284.           if (!m)
  285.             warn("local and central headers differ for ", z->zname);
  286.           m = 1;
  287.           sprintf(errbuf, " offset %d--local = %02x, central = %02x",
  288.                   n, (uch)u[n], (uch)b[n+2]);
  289.           warn(errbuf, "");
  290.         }
  291.       if (m)
  292.         return ZE_FORM;
  293.       b[CENFLG+1] = r;                  /* Restore reserved bits */
  294.  
  295.       /* Overwrite local header with translated central header */
  296.       z->vem = SH(CENVEM + b);
  297.       z->ver = SH(CENVER + b);
  298.       z->flg = SH(CENFLG + b);          /* may be different from z->lflg */
  299.       z->how = SH(CENHOW + b);
  300.       z->tim = LG(CENTIM + b);          /* time and date into one long */
  301.       z->crc = LG(CENCRC + b);
  302.       z->siz = LG(CENSIZ + b);
  303.       z->len = LG(CENLEN + b);
  304.       z->nam = SH(CENNAM + b);
  305.       z->cext = SH(CENEXT + b);         /* may be different from z->ext */
  306.       z->com = SH(CENCOM + b);
  307.       z->dsk = SH(CENDSK + b);
  308.       z->att = SH(CENATT + b);
  309.       z->atx = LG(CENATX + b);
  310.       if (z->off != LG(CENOFF + b))
  311.       {
  312.         warn("local offset in central header incorrect for ", z->zname);
  313.         return ZE_FORM;
  314.       }
  315.  
  316.       /* Compare name and extra fields and read comment field */
  317.       if ((t = malloc(z->nam)) == NULL)
  318.         return ZE_MEM;
  319.       if (fread(t, z->nam, 1, f) != 1)
  320.       {
  321.         free((voidp *)t);
  322.         return ferror(f) ? ZE_READ : ZE_EOF;
  323.       }
  324.       if (memcmp(t, z->zname, z->nam))
  325.       {
  326.         free((voidp *)t);
  327.         warn("names in local and central differ for ", z->zname);
  328.         return ZE_FORM;
  329.       }
  330.       free((voidp *)t);
  331.       if (z->cext)
  332.       {
  333.         if ((z->cextra = malloc(z->cext)) == NULL)
  334.           return ZE_MEM;
  335.         if (fread(z->cextra, z->cext, 1, f) != 1)
  336.         {
  337.           free((voidp *)(z->cextra));
  338.           return ferror(f) ? ZE_READ : ZE_EOF;
  339.         }
  340.         if (z->ext == z->cext && memcmp(z->extra, z->cextra, z->ext) == 0)
  341.         {
  342.           free((voidp *)(z->cextra));
  343.           z->cextra = z->extra;
  344.         }
  345.       }
  346.       if (z->com)
  347.       {
  348.         if ((z->comment = malloc(z->com)) == NULL)
  349.           return ZE_MEM;
  350.         if (fread(z->comment, z->com, 1, f) != 1)
  351.         {
  352.           free((voidp *)(z->comment));
  353.           return ferror(f) ? ZE_READ : ZE_EOF;
  354.         }
  355.       }
  356.  
  357.       /* Note oddities */
  358.       if (verbose)
  359.       {
  360.         if (z->vem != 10 && z->vem != 11 &&
  361.             (n = z->vem >> 8) != 3 && n != 2 && n != 6)
  362.         {
  363.           sprintf(errbuf, "made by version %d.%d on system type %d: ",
  364.                   (z->vem & 0xff) / 10, (z->vem & 0xff) % 10, z->vem >> 8);
  365.           warn(errbuf, z->zname);
  366.         }
  367.         if (z->ver != 10 && z->ver != 11)
  368.         {
  369.           sprintf(errbuf, "needs unzip %d.%d on system type %d: ",
  370.                   (z->ver & 0xff) / 10, (z->ver & 0xff) % 10, z->ver >> 8);
  371.           warn(errbuf, z->zname);
  372.         }
  373.         if (z->flg != z->lflg)
  374.         {
  375.           sprintf(errbuf, "local flags = 0x%04x, central = 0x%04x: ",
  376.                   z->lflg, z->flg);
  377.           warn(errbuf, z->zname);
  378.         }
  379.         else if (z->flg & ~7)
  380.         {
  381.           sprintf(errbuf, "undefined bits used in flags = 0x%04x: ", z->flg);
  382.           warn(errbuf, z->zname);
  383.         }
  384.         if (z->how > IMPLODE)
  385.         {
  386.           sprintf(errbuf, "unknown compression method %u: ", z->how);
  387.           warn(errbuf, z->zname);
  388.         }
  389.         if (z->dsk)
  390.         {
  391.           sprintf(errbuf, "starts on disk %u: ", z->dsk);
  392.           warn(errbuf, z->zname);
  393.         }
  394.         if (z->att & ~1)
  395.         {
  396.           sprintf(errbuf, "unknown internal attributes = 0x%04x: ", z->att);
  397.           warn(errbuf, z->zname);
  398.         }
  399.         if (((n = z->vem >> 8) != 3) && n != 2 && z->atx & ~0xffL)
  400.         {
  401.           sprintf(errbuf, "unknown external attributes = 0x%08lx: ", z->atx);
  402.           warn(errbuf, z->zname);
  403.         }
  404.         if (z->ext || z->cext)
  405.           if (z->ext == z->cext && z->extra == z->cextra)
  406.           {
  407.             sprintf(errbuf, "has %d bytes of extra data: ", z->ext);
  408.             warn(errbuf, z->zname);
  409.           }
  410.           else
  411.           {
  412.             sprintf(errbuf,
  413.                     "local extra (%d bytes) != central extra (%d bytes): ",
  414.                     z->ext, z->cext);
  415.             warn(errbuf, z->zname);
  416.           }
  417.       }
  418.  
  419.       /* Clear actions */
  420.       z->mark = 0;
  421.       z->trash = 0;
  422.  
  423.       /* Update file offset */
  424.       p += 4 + CENHEAD + z->nam + z->cext + z->com;
  425.  
  426.       /* Advance to next header structure */
  427.       z = z->nxt;
  428.  
  429.       /* Read next signature */
  430.       if (fread(b, 4, 1, f) != 1)
  431.         return ferror(f) ? ZE_READ : ZE_EOF;
  432.     }
  433.     
  434.     /* Read end header */
  435.     if (z != NULL || LG(b) != ENDSIG)
  436.     {
  437.       warn("missing end signature--probably not a zip file (did you", "");
  438.       warn("remember to use binary mode when you transferred it?)", "");
  439.       return ZE_FORM;
  440.     }
  441.     if (fread(b, ENDHEAD, 1, f) != 1)
  442.       return ferror(f) ? ZE_READ : ZE_EOF;
  443.     if (SH(ENDDSK + b) || SH(ENDBEG + b) ||
  444.         SH(ENDSUB + b) != SH(ENDTOT + b))
  445.       warn("multiple disk information ignored", "");
  446.     if (zcount != SH(ENDSUB + b))
  447.     {
  448.       warn("count in end of central directory incorrect", "");
  449.       return ZE_FORM;
  450.     }
  451.     if (LG(ENDSIZ + b) != p - s)
  452.     {
  453.       warn("central directory size is incorrect", "");
  454.       return ZE_FORM;
  455.     }
  456.     if (LG(ENDOFF + b) != s)
  457.     {
  458.       warn("central directory start is incorrect", "");
  459.       return ZE_FORM;
  460.     }
  461.     cenbeg = s;
  462.     zcomlen = SH(ENDCOM + b);
  463.     if (zcomlen)
  464.     {
  465.       if ((zcomment = malloc(zcomlen)) == NULL)
  466.         return ZE_MEM;
  467.       if (fread(zcomment, zcomlen, 1, f) != 1)
  468.       {
  469.         free((voidp *)zcomment);
  470.         return ferror(f) ? ZE_READ : ZE_EOF;
  471.       }
  472.     }
  473.     if (zipbeg)
  474.     {
  475.       sprintf(errbuf, " has a preamble of %ld bytes", zipbeg);
  476.       warn(zipfile, errbuf);
  477.     }
  478.     if (getc(f) != EOF)
  479.       warn("garbage at end of zip file ignored", "");
  480.  
  481.     /* Done with zip file for now */
  482.     fclose(f);
  483.     
  484.     /* If one or more files, sort by name */
  485.     if (zcount)
  486.     {
  487.       if ((x = zsort =
  488.           (struct zlist far **)malloc(zcount * sizeof(struct zlist far *))) ==
  489.           NULL)
  490.         return ZE_MEM;
  491.       for (z = zfiles; z != NULL; z = z->nxt)
  492.         *x++ = z;
  493.       qsort((char *)zsort, zcount, sizeof(struct zlist far *), zqcmp);
  494.     }
  495.   }
  496.   return ZE_OK;
  497. }
  498.  
  499.  
  500. int putlocal(z, f)
  501. struct zlist far *z;    /* zip entry to write local header for */
  502. FILE *f;                /* file to write to */
  503. /* Write a local header described by *z to file *f.  Return an error code
  504.    in the ZE_ class. */
  505. {
  506.   PUTLG(LOCSIG, f);
  507.   PUTSH(z->ver, f);
  508.   PUTSH(z->lflg, f);
  509.   PUTSH(z->how, f);
  510.   PUTLG(z->tim, f);
  511.   PUTLG(z->crc, f);
  512.   PUTLG(z->siz, f);
  513.   PUTLG(z->len, f);
  514.   PUTSH(z->nam, f);
  515.   PUTSH(z->ext, f);
  516.   if (fwrite(z->zname, 1, z->nam, f) != z->nam ||
  517.       z->ext && fwrite(z->extra, 1, z->ext, f) != z->ext)
  518.     return ZE_TEMP;
  519.   return ZE_OK;
  520. }
  521.  
  522.  
  523. int putcentral(z, f)
  524. struct zlist far *z;    /* zip entry to write central header for */
  525. FILE *f;                /* file to write to */
  526. /* Write a central header described by *z to file *f.  Return an error code
  527.    in the ZE_ class. */
  528. {
  529.   PUTLG(CENSIG, f);
  530.   PUTSH(z->vem, f);
  531.   PUTSH(z->ver, f);
  532.   PUTSH(z->flg, f);
  533.   PUTSH(z->how, f);
  534.   PUTLG(z->tim, f);
  535.   PUTLG(z->crc, f);
  536.   PUTLG(z->siz, f);
  537.   PUTLG(z->len, f);
  538.   PUTSH(z->nam, f);
  539.   PUTSH(z->cext, f);
  540.   PUTSH(z->com, f);
  541.   PUTSH(z->dsk, f);
  542.   PUTSH(z->att, f);
  543.   PUTLG(z->atx, f);
  544.   PUTLG(z->off, f);
  545.   if (fwrite(z->zname, 1, z->nam, f) != z->nam ||
  546.       z->cext && fwrite(z->cextra, 1, z->cext, f) != z->cext ||
  547.       z->com && fwrite(z->comment, 1, z->com, f) != z->com)
  548.     return ZE_TEMP;
  549.   return ZE_OK;
  550. }
  551.  
  552.  
  553. int putend(n, s, c, m, z, f)
  554. int n;                  /* number of entries in central directory */
  555. ulg s, c;               /* size and offset of central directory */
  556. extent m;               /* length of zip file comment (0 if none) */
  557. char *z;                /* zip file comment if m != 0 */
  558. FILE *f;                /* file to write to */
  559. /* Write the end of central directory data to file *f.  Return an error code
  560.    in the ZE_ class. */
  561. {
  562.   PUTLG(ENDSIG, f);
  563.   PUTSH(0, f);
  564.   PUTSH(0, f);
  565.   PUTSH(n, f);
  566.   PUTSH(n, f);
  567.   PUTLG(s, f);
  568.   PUTLG(c, f);
  569.   PUTSH(m, f);
  570.   if (m && fwrite(z, 1, m, f) != m)
  571.     return ZE_TEMP;
  572.   return ZE_OK;
  573. }
  574.  
  575.  
  576. #ifndef UTIL
  577.  
  578. local char *cutpath(p)
  579. char *p;                /* path string */
  580. /* Cut the last path component off the name *p in place.  Return p. */
  581. {
  582.   char *r;              /* pointer to last path delimiter */
  583.  
  584. #ifdef VMS                      /* change [w.x.y]z to [w.x]y.DIR */
  585.   if ((r = strrchr(p, ']')) != NULL)
  586.   {
  587.     *r = 0;
  588.     if ((r = strrchr(p, '.')) != NULL)
  589.     {
  590.       *r = ']';
  591.       strcat(r, ".DIR");        /* this assumes a little padding--see PAD */
  592.     }
  593.     else
  594.       *p = 0;
  595.   }
  596.   else
  597.     *p = 0;
  598. #else /* !VMS */                /* change w/x/y/z to w/x/y */
  599.   if ((r = strrchr(p, '/')) != NULL)
  600.     *r = 0;
  601.   else
  602.     *p = 0;
  603. #endif /* ?VMS */
  604.   return p;
  605. }
  606.  
  607. int trash()
  608. /* Delete the compressed files and the directories that contained the deleted
  609.    files, if empty.  Return an error code in the ZE_ class.  Failure of
  610.    destroy() or deletedir() is ignored. */
  611. {
  612.   extent i;             /* counter on deleted names */
  613.   extent k;             /* number of deleted directories this pass */
  614.   extent n;             /* number of deleted names left to handle */
  615.   struct zlist far **s; /* table of zip entries to handle, sorted */
  616.   struct zlist far *z;  /* current zip entry */
  617.  
  618.   /* Count and delete marked names */
  619.   n = 0;
  620.   for (z = zfiles; z != NULL; z = z->nxt)
  621.     if (z->mark || z->trash)
  622.     {
  623.       z->mark = 1;
  624.       n++;
  625.       if (verbose)
  626.         printf("zip diagnostic: trashing file %s\n", z->name);
  627.       destroy(z->name);
  628.     }
  629.  
  630.   /* Try to delete all paths that lead up to marked names */
  631.   if (n)
  632.   {
  633.     if ((s = (struct zlist far **)malloc((n+1)*sizeof(struct zlist far *))) ==
  634.         NULL ||
  635.         (s[0] = (struct zlist far *)farmalloc(sizeof(struct zlist))) == NULL)
  636.       return ZE_MEM;
  637.     s[0]->name = "";
  638.     s++;
  639.     do {
  640.       n = k = 0;
  641.       for (z = zfiles; z != NULL; z = z->nxt)
  642.         if (z->mark)
  643.           s[n++] = z;
  644.       qsort((char *)s, n, sizeof(struct zlist far *), zqcmp);
  645.       for (i = 0; i < n; i++)
  646.         if (*cutpath(s[i]->name) && strcmp(s[i]->name, s[i-1]->name))
  647.         {
  648.           if (verbose)
  649.             printf("zip diagnostic: trashing directory %s\n", s[i]->name);
  650.           deletedir(s[i]->name);
  651.           k++;
  652.         }
  653.         else
  654.           s[i]->mark = 0;
  655.     } while (k);
  656.     farfree((voidp far *)((--s)[0]));
  657.     free((voidp *)s);
  658.   }
  659.   return ZE_OK;
  660. }
  661.  
  662. #endif /* !UTIL */
  663.